package com.zhang.spring.boot.mongo;


import org.bson.BsonDocument;
import org.bson.Document;
import org.bson.codecs.pojo.annotations.BsonProperty;

import java.util.List;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 *
 */
public class PartTree {

    private static final String PREFIX="{";
    private static final String POSTFIX="}";


    protected final String source;
    protected final Class<?> clazz;

    protected  ConjunctionEnum conjunction;
    protected PredicateEnum predicate;

    protected String columnName;

    protected List<PartTree> child;

    public PartTree(String source, Class<?> clazz) {
        this.source = source;
        this.clazz = clazz;

        String[] splitOr = source.split("Or");
        String[] splitAnd = source.split("And");
        //split by "or/and"
        if (splitOr.length>1){
            conjunction=ConjunctionEnum.Or;
            buildChild(splitOr);
        }else if(splitAnd.length >1){
            conjunction=ConjunctionEnum.And;
            buildChild(splitAnd);
        }else {
            // no conjunction exites , it is a predicate node
            predicate = this.getPredicate(source);
            int preIndex = source.lastIndexOf(predicate.getPredicate());
            if (preIndex == -1){
                preIndex= source.length();
            }
            String filedName = source.substring(0,preIndex);
            char chars = filedName.charAt(0);
            if (chars >'A' && chars <'Z'){
                chars +=32;
            }
            StringBuilder sb = new StringBuilder(filedName);
            sb.setCharAt(0,chars);
            filedName= sb.toString();
            columnName = this.getColumnName(clazz,filedName);

        }


    }

    public static void main(String[] args) {
        String source = "x";
        PartTree p = new PartTree(source, Foo.class);
        System.out.println(p.getDocument().toJson());

        String source1 = "XAndY";
        PartTree p1 = new PartTree(source1, Foo.class);
        System.out.println(p1.getDocument().toJson());

        String source2 = "xOry";
        PartTree p2 = new PartTree(source2, Foo.class);
        System.out.println(p2.getDocument().toJson());

    }

    private PredicateEnum getPredicate(String source){
        return  Stream.of(PredicateEnum.values()).filter(predicate -> {
            Pattern p = Pattern.compile("("+predicate.getPredicate()+")$");
            Matcher matcher = p.matcher(source);
            return matcher.find();
        }).findFirst().orElse(PredicateEnum.Is);
    }

    private String getColumnName(Class<?> entityClass, String filedName)  {
        return Stream.of(entityClass.getDeclaredFields())
                /*.filter(field -> {
                    char chars = filedName.charAt(0);
                    if (chars >'A' && chars <'Z'){
                        chars +=32;
                    }
                    StringBuilder sb = new StringBuilder(filedName);
                    sb.setCharAt(0,chars);
                    return sb.toString().equals(field.getName());
                })*/
                .map(field -> field.getAnnotation(BsonProperty.class))
                .filter( o -> null !=o)
                .map(BsonProperty::value).findFirst().orElse(filedName);

    }

     public Document getDocument(){
        Document document = new Document();
        if(ConjunctionEnum.And == conjunction){
            child.stream().map( c -> c.getDocument())
                    .reduce(document,(acc,doc)-> {
                        for(String key:doc.keySet()){
                            acc.append(key,doc.get(key));
                        }
                        return acc;
                    });
        }else if(ConjunctionEnum.Or == conjunction){
            List<Document> list=child.stream().map(c-> c.getDocument()).collect(Collectors.toList());
            document.put(conjunction.getExpression(),list);
        } else{
            Document expression = new Document(predicate.getExpression(),"?");
            document.put(this.columnName,expression);
        }
        return document;
     }

    private void buildChild(String[] split){
        this.child=Stream.of(split)
                .map(s -> new PartTree(s,clazz))
                .collect(Collectors.toList());
    }
}
